#![allow(non_snake_case)]
use leptos::{ev, html, prelude::*};
use serde::{Deserialize, Serialize};
use shq_common::prelude::{Size, TreeNode};
use std::{fmt::Display, hash::Hash};
use thaw_utils::{class_list, ArcOneCallback, BoxOneCallback, ComponentRef, Model};

use crate::{InputPrefix, InputRef, InputSuffix, ValidType};
use icondata as i;
use leptos_icons::*;
mod dropdown;
mod tree_items;
mod multi_select;
mod multi_tree;
// mod multi_tree2;
mod select_raw;
mod tree_select;
pub use dropdown::*;
pub use multi_select::*;
pub use multi_tree::*;
pub use select_raw::*;
pub use tree_select::*;
pub use tree_items::*;

#[derive(Clone, PartialEq, Eq, Hash,Serialize, Deserialize)]
pub struct SelectOption<T> {
    pub label: String,
    pub value: T,
}
impl<T> SelectOption<T> {
    pub fn new(label: impl AsRef<str>, value: T) -> Self {
        Self {
            label: label.as_ref().to_owned(),
            value,
        }
    }
}
impl From<TreeNode> for SelectOption<String> {
    fn from(value: TreeNode) -> Self {
        SelectOption {
            label: value.title,
            value: value.key,
        }
    }
}
impl From<&TreeNode> for SelectOption<String> {
    fn from(value: &TreeNode) -> Self {
        SelectOption {
            label: value.title.clone(),
            value: value.key.clone(),
        }
    }
}

#[component]
pub fn Select<T>(
    #[prop(optional, into)] value: Model<Option<T>>,
    #[prop(into)] options: MaybeProp<Vec<SelectOption<T>>>,
    #[prop(optional, into)] on_change: Option<BoxOneCallback<Option<T>>>,
    #[prop(optional, into)] placeholder: MaybeProp<String>,
    #[prop(optional, into)] valid_type: MaybeProp<ValidType>,
    #[prop(default=true.into(), into)] border: MaybeProp<bool>,
    #[prop(optional, into)] can_filter: MaybeProp<bool>,
    #[prop(optional, into)] size: MaybeProp<Size>,
    #[prop(optional)] input_prefix: Option<InputPrefix>,
    #[prop(optional)] input_suffix: Option<InputSuffix>,
    #[prop(optional, into)] disabled: MaybeProp<bool>,
    #[prop(optional)] comp_ref: ComponentRef<InputRef>,
    #[prop(optional)] inner_max_width: u32,
    #[prop(optional)] is_init_focus: bool,
) -> impl IntoView
where
    T: Eq + Hash + Clone + Ord + Display + Send + Sync + 'static,
{
    let is_show_menu: RwSignal<bool> = RwSignal::new(false);
    let store_options = StoredValue::new(options.get().unwrap_or_default());
    let select_option_label = Memo::new(move |_| match value.get() {
        Some(value) => store_options.with_value(|opts| {
            opts.iter()
                .find(|v| v.value == value)
                .map_or(String::new(), |v| v.label.clone())
        }),
        None => String::new(),
    });
    let label_class = move || {
        if border.get().unwrap_or_default() {
            let class = valid_type.get().unwrap_or_default().as_color_str();
            format!("input input-bordered input-{}", class)
        } else {
            "".to_owned()
        }
    };
    let on_select= ArcOneCallback::new(move |val: Option<T>| {
        value.set(val.clone());
        if let Some(on_change) = on_change.as_ref() {
            on_change(val.clone());
        }
    });
    let input_ref = NodeRef::<html::Input>::new();
    input_ref.on_load(move |_| {
        comp_ref.load(InputRef::new(input_ref));
    });
    Effect::new(
        move || {
            if let Some(input_el) = input_ref.get_untracked() {
                if is_init_focus {
                    _ = input_el.focus();
                }
            }
        }
    );
    let on_mousedown = move |event: ev::MouseEvent| {
        let el: web_sys::HtmlElement = event_target(&event);
        if el.tag_name() != "INPUT" {
            event.prevent_default();
            if let Some(input_ref) = input_ref.get_untracked() {
                input_ref.focus().unwrap();
            }
        }
    };
    let style_width = if inner_max_width > 0 {
        format!("max-width:{}px", inner_max_width)
    } else {
        "".to_owned()
    };
    let values=if let Some(v) = value.get() {
        vec![v]
    } else {
        vec![]
    };
    view! {
        <SelectRaw
            values
            options=options.get()
            can_filter
            on_select
            is_show_menu
        >
            <label
                class=class_list!["flex items-center gap-2 w-full max-w-xs",label_class,move||size.get().unwrap_or_default().to_class("input")]
                on:mousedown=on_mousedown
            >
                {if let Some(sp) = input_prefix.and_then(|p| p.if_.then_some(p)) {
                    Some((sp.children)())
                } else {
                    None
                }}
                <input type="text" class="grow" style=style_width
                    class=(["outline-0","border-0","w-full","ml-1","mr-1","bg-inherit"],move||!border.get().unwrap_or_default())
                    on:focus=move|_|is_show_menu.set(true)
                    placeholder=move || placeholder.get()
                    prop:value=move || select_option_label.get()
                    disabled=move ||disabled.get()
                    readonly=true
                    node_ref=input_ref
                />
                <Icon icon=i::AiCaretDownOutlined/>
                {if let Some(sp) = input_suffix.and_then(|s| s.if_.then_some(s)) {
                    Some((sp.children)())
                } else {
                    None
                }}
            </label>
        </SelectRaw>
    }
}
